# model

# responsible for the synchronization of the available cloud-based storage-services, if they are not synchronously

from google.appengine.ext import webapp

from boto.s3.resumable_download_handler import *

from controller.BuildStorageInfo import *

from Utilities import *

class SynchronizeStorages(webapp.RequestHandler):
    def __init__(self):
        self.__errorWhileUpload = 'false'
    
    def post(self):
        utilities = Utilities()
        
        # get some attributes
        username = utilities.getUsername()
        bucketname = utilities.getBucketname(username)
        
        # check if there is an user logged-in
        if username:
            # get some basic attributes
            userText = utilities.getUserText()
            url = utilities.getLogout(self)
            urlText = utilities.getUrlTextLogout()
            
            self.prepareForSync(utilities, username, bucketname, userText, url, urlText)
        else:
            self.redirect('/')
        
    # connect with the storages and prepare the lists (keys and md5)
    def prepareForSync(self, utilities, username, bucketname, userText, url, urlText):
        # get the available credentials
        credentials = utilities.getCredentials(username)
        credentialAmount = credentials.count()
        results = credentials.fetch(100)
        
        # these attributes are needed for the sync-check below
        isAmazonAvailable = 'false'
        isGoogleAvailable = 'false'
        isOpenStackAvailable = 'false'
        amazonStorageConnection = None
        googleStorageConnection = None
        openStackStorageConnection = None
        
        # some basic attributes
        amazon = utilities.getAmazonText()
        google = utilities.getGoogleText()
        openStack = utilities.getOpenStackText()
        
        # check if there are available storage-services
        if credentialAmount > 0:
            for result in results:
                regionname = result.regionname
                
                # connect with amazon-storage-service to get the key- and md5-list
                if regionname == amazon:
                    amazonStorageConnection = utilities.getStorageServiceConnectionByResult(username, result)
                    amazonKeyList = utilities.getBucketKeyList(amazonStorageConnection, bucketname)
                    amazonMD5Checksums = utilities.getMD5Checksums(username, regionname, amazonKeyList)
                    isAmazonAvailable = 'true'
                    
                # connect with google-storage-service to get the key- and md5-list
                if regionname == google:
                    googleStorageConnection = utilities.getStorageServiceConnectionByResult(username, result)
                    googleKeyList = utilities.getBucketKeyList(googleStorageConnection, bucketname)
                    googleMD5Checksums = utilities.getMD5Checksums(username, regionname, googleKeyList)
                    isGoogleAvailable = 'true'

                # connect with open-stack-storage-service to geht the key- and md5-list
                if regionname == openStack:
                    openStackStorageConnection = utilities.getStorageServiceConnectionByResult(username, result)
                    openStackKeyList = utilities.getBucketKeyList(openStackStorageConnection, bucketname)
                    # original way to get the md5-checksums
                    # DO NOT DELETE!!
                    # openStackMD5Checksums = utilities.getMD5Checksums(username, regionname, openStackKeyList)
                    
                    # workaround to get the md5-checksums for the key in the openStack-storage from the local database (only for openStack)
#                    openStackMD5Checksums = utilities.getMD5ChecksumListForOpenStack()
                    openStackMD5Checksums = utilities.getMD5ChecksumListByAttributesForOpenStack(openStackKeyList)
                    isOpenStackAvailable = 'true'

            # continue only, if there is an open connection to the openStack-object-storage
            if isOpenStackAvailable == 'true':
                if isAmazonAvailable == 'true':
                    # get the key- and md5-lists
                    amazonKeyList = utilities.getBucketKeyList(amazonStorageConnection, bucketname)
                    amazonMD5Checksums = utilities.getMD5Checksums(username, regionname, amazonKeyList)
                    
                    # check if the keys (based on the md5-checksums) in amazon-storage contained in openStack-storage
                    self.checkForSync(utilities, amazon, openStack, username, bucketname, openStackStorageConnection, amazonMD5Checksums, openStackMD5Checksums, amazonKeyList, userText, url, urlText)                            

                if isGoogleAvailable == 'true':
                    # get the key- and md5-lists
                    googleKeyList = utilities.getBucketKeyList(googleStorageConnection, bucketname)
                    googleMD5Checksums = utilities.getMD5Checksums(username, regionname, googleKeyList)
                    
                    # check if the keys (based on the md5-checksums) in google-storage contained in openStack-storage
                    self.checkForSync(utilities, google, openStack, username, bucketname, openStackStorageConnection, googleMD5Checksums, openStackMD5Checksums, googleKeyList, userText, url, urlText)

            # continue only, if there is an open connection to the amazon-storage
            if isAmazonAvailable == 'true':
                if isGoogleAvailable == 'true':
                    # get the key- and md5-lists
                    googleKeyList = utilities.getBucketKeyList(googleStorageConnection, bucketname)
                    googleMD5Checksums = utilities.getMD5Checksums(username, regionname, googleKeyList)
                    
                    # check if the keys (based on the md5-checksums) in google-storage contained in amazon-storage
                    self.checkForSync(utilities, google, amazon, username, bucketname, amazonStorageConnection, googleMD5Checksums, amazonMD5Checksums, googleKeyList, userText, url, urlText)

                if isOpenStackAvailable == 'true':
                    # get the key- and md5-lists
                    openStackKeyList = utilities.getBucketKeyList(openStackStorageConnection, bucketname)
                    openStackMD5Checksums = utilities.getMD5ChecksumListByAttributesForOpenStack(openStackKeyList)
                    
                    # check if the keys (based on the md5-checksums) in openStack-storage contained in amazon-storage
                    self.checkForSync(utilities, openStack, amazon, username, bucketname, amazonStorageConnection, openStackMD5Checksums, amazonMD5Checksums, openStackKeyList, userText, url, urlText)

            # continue only, if there is an open connection to the google-storage
            if isGoogleAvailable == 'true':
                if isAmazonAvailable == 'true':
                    # get the key- and md5-lists
                    amazonKeyList = utilities.getBucketKeyList(amazonStorageConnection, bucketname)
                    amazonMD5Checksums = utilities.getMD5Checksums(username, regionname, amazonKeyList)
                    
                    # check if the keys (based on the md5-checksums) in amazon-storage contained in google-storage
                    self.checkForSync(utilities, amazon, google, username, bucketname, googleStorageConnection, amazonMD5Checksums, googleMD5Checksums, amazonKeyList, userText, url, urlText)

                if isOpenStackAvailable == 'true':
                    # get the key- and md5-lists
                    openStackKeyList = utilities.getBucketKeyList(openStackStorageConnection, bucketname)
                    openStackMD5Checksums = utilities.getMD5ChecksumListByAttributesForOpenStack(openStackKeyList)
                         
                    # check if the keys (based on the md5-checksums) in openStack-storage contained in google-storage
                    self.checkForSync(utilities, openStack, google, username, bucketname, googleStorageConnection, openStackMD5Checksums, googleMD5Checksums, openStackKeyList, userText, url, urlText)
                
            # when everything is done...
            # check, if the storages are synchronously - therefore the key-lists and md5-lists must be fetched again...
            self.compareStorages(utilities, username, bucketname, userText, url, urlText, amazonStorageConnection, googleStorageConnection, openStackStorageConnection)
        else:
            self.redirect('/selectdata')

      
    # do some checks and call the perform-method for the synchronization if necessary
    # NOTE:
    #    primaryReginname:           the regionname which is needed for the case differentiation to get the md5-value
    #    secondaryRegionname:        the regionname which is needed for the case differentiation for the file-upload
    #    primaryMD5Checksums:        the list of checksums, which is be compared against the secondaryMD5Checksums-list
    #    secondaryMD5Checksums:      the other part of checksums, which is needed for the comparison
    def checkForSync(self, utilities, primaryRegionname, secondaryRegionname, username, bucketname, storageConnection, primaryMD5Checksums, secondaryMD5Checksums, keyList, userText, url, urlText):
        # check if the keys (based on the md5-checksums) in first-storage contained in second-storage
        for md5Checksum in primaryMD5Checksums:
            isInStorageContained = secondaryMD5Checksums.__contains__(md5Checksum)
            
            # if a key is not contained in the second-storage, then upload it
            if isInStorageContained == False:
                # before the upload is performed, the file must be fetched
                keyListLength = len(keyList)
                if keyListLength > 0:
                    for i in range(keyListLength):
                        if primaryRegionname == utilities.getOpenStackText():
                            filename = keyList[i].name 
                            fileSize = keyList[i].size
                            md5 = utilities.getMD5ByKeynameAndFileSize(filename, fileSize)
                        
                        if primaryRegionname == utilities.getAmazonText() or primaryRegionname == utilities.getGoogleText():
                            md5 = utilities.convertMD5Outline(username, keyList, i)
                        
                        isEqual = cmp(md5, md5Checksum)
                        
                        # if the checksums are equal, the value is 0
                        if isEqual == 0:
                            # get the concerned key (file) and upload it
                            self.performSync(utilities, primaryRegionname, secondaryRegionname, username, bucketname, storageConnection, keyList, md5, userText, url, urlText)
                        
                        #should only be performed, when an error occurred while try to synchronize a file..
                        if self.__errorWhileUpload == 'true':
                            break
                                                        
    # perform the synchronization by read the missing file and write it in the other storage
    def performSync(self, utilities, primaryRegionname, secondaryRegionname, username, bucketname, storageConnection, keyList, md5, userText, url, urlText):
        try:
            # get the concerned key (file) and upload it
            if primaryRegionname == utilities.getOpenStackText():
                keyname = utilities.getKeynameForOpenStackKeyByMD5(md5)
                keySize = utilities.getFileSizeForOpenStackKeyByMD5(md5)
                key = utilities.getBucketKeyByKeynameAndFileSize(str(keyname), keySize, keyList)
            
            if primaryRegionname == utilities.getAmazonText() or primaryRegionname == utilities.getGoogleText():
                key = utilities.getBucketKeyByChecksum(username, keyList, md5)
                keySize = key.size
            
            keyname = key.name
#            userFile = key.read()
            userFile = key.read(keySize)

            # get some necessary attributes
            keySize = utilities.getKeySizeByChecksum(username, primaryRegionname, keyList, md5)
            lastMod = utilities.getLastModByChecksum(username, primaryRegionname, keyList, md5)

            utilities.syncUploadFile(secondaryRegionname, storageConnection, bucketname, userFile, keyname, keySize, lastMod, md5)
        except:
            # set the local private variable for error 'true'
            self.__errorWhileUpload = 'true'
            
#            hintText= _("An error occurred - synchronization couldn't be performed")
            hintText = "An error occurred - synchronization couldn't be performed"
            hintColor = 'red'
            
            isSync = 'false'
            self.callBuilder(userText, username, url, urlText, hintText, hintColor, keyList, isSync)
        
    # perform the check, if the storage-services now are synchronously
    def compareStorages(self, utilities, username, bucketname, userText, url, urlText, amazonStorageConnection=None, googleStorageConnection=None, openStackStorageConnection=None):
        # initialize the key-lists
        amazonKeyList = []
        googleKeyList = []
        openStackKeyList = []
        
        # initialize the md5-checksum-lists
        amazonMD5Checksums = None
        googleMD5Checksums = None
        openStackMD5Checksums = None
        
        if amazonStorageConnection != None:
            amazonKeyList = utilities.getBucketKeyList(amazonStorageConnection, bucketname)
            amazonMD5Checksums = utilities.getMD5Checksums(username, utilities.getAmazonText(), amazonKeyList)
        
        if googleStorageConnection != None:
            googleKeyList = utilities.getBucketKeyList(googleStorageConnection, bucketname)
            googleMD5Checksums = utilities.getMD5Checksums(username, utilities.getGoogleText(), googleKeyList)
            
        if openStackStorageConnection != None:
            openStackKeyList = utilities.getBucketKeyList(openStackStorageConnection, bucketname)
            # original way to get the md5-checksums
            # DO NOT DELETE!!
            #openStackMD5Checksums = utilities.getMD5Checksums(username, utilities.getOpenStackText(), openStackKeyList)
            
            # workaround to get the md5-checksums (only for openstack)
            openStackMD5Checksums = utilities.getMD5ChecksumListByAttributesForOpenStack(openStackKeyList)
       
        isSync = utilities.compareChecksums(amazonMD5Checksums, googleMD5Checksums, openStackMD5Checksums)
        
        if isSync == 'true':
#            hintText = _("The storage-services couldn't be synchronized. Please try again and click the button below the table.")
            hintText = "The available storage-services are synchronously."
            hintColor = 'green'
        
            keyList = amazonKeyList
            if amazonKeyList == [] or amazonKeyList == None:
                keyList = googleKeyList
                if googleKeyList == [] or googleKeyList == None:
                    keyList = openStackKeyList
                    if openStackKeyList == [] or openStackKeyList == None:
                        keyList = []
            
            self.callBuilder(userText, username, url, urlText, hintText, hintColor, keyList, isSync)
        
        # produce multiple view-creation...
        if isSync == 'false':
#            hintText = _("The storage-services couldn't be synchronized. Please try again and click the button below the table.")
            hintText = "The storage-services couldn't be synchronized. Please try again and click the button below the table."
            hintColor = 'red'
            
            keyList = amazonKeyList
            if amazonKeyList == [] or amazonKeyList == None:
                keyList = googleKeyList
                if googleKeyList == [] or googleKeyList == None:
                    keyList = openStackKeyList
                    if openStackKeyList == [] or openStackKeyList == None:
                        keyList = []
            
            self.callBuilder(userText, username, url, urlText, hintText, hintColor, keyList, isSync)
        
    # responsible for the call of the get-method of the BuildStorageInfo-Class
    def callBuilder(self, userText, username, url, urlText, hintText, hintColor, keyList, isSync=''):
        templateValues = {
                          'userText': userText,
                          'user': username,
                          'urlText': urlText,
                          'url': url,
                          'urlText': urlText,
                          'hintText': hintText,
                          'hintColor': hintColor
                          }
        builder = BuildStorageInfo()
        builder.get(self, templateValues, keyList, isSync)

    # responsible for the hint-text 'no storage connection...'
    def getNoStorageConnectionText(self):
#        hintText = _('An error occurred - there is no open storage connection available for ')
        hintText = 'An error occurred - there is no open storage connection available for '
        return hintText
    
